package simulation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import parser.*;
import Executor.*;
public class Simulation {
	protected List<Node> nodeList = new ArrayList<Node>();//= root.getnodeList();//get a bottom-up tree
	protected HashMap<String,List<String>> ancestorMap;
	public static void main(String[] args)
	{
		List<Root> rootList =parser.Parser.parse("//book[author//lastname]/author[name//lastname]");
		Root root = rootList.get(0); //get the root after parsing
		toString((Node) root,0);
		HashMap<String,List<String>> testMap = new HashMap<String,List<String>>();
		Simulation simulator = new Simulation();
		simulator.simulate(root,testMap);
		simulator.printOutDetails(root);
	}
	private void printOutDetails(Root root) {
		// TODO Auto-generated method stub
		for(Node node : nodeList)
		{        
			System.out.println(node.getType()+ ". ID: "+node.toString() +")");
			System.out.println("	sim:");
			for(Node child : node.getSimList())
			{
				System.out.print("		");
				System.out.println(child.getType()+ ". ID: "+child.toString() +")");
			}
			System.out.println("	cPar:");
			for(Node child : node.getCPar())
			{
				System.out.print("		");
				System.out.println(child.getType()+ ". ID: "+child.toString() +")");
			}
			System.out.println("	auganc:");
			for(Node auganc : node.getAuganc())
			{
				System.out.print("		");
				System.out.println(auganc.getType()+ ". ID: "+auganc.toString() +")");
			}
		}
	}
	
    public void simulate(Node root, HashMap<String,List<String>> requiredAncestor)
    {   
	   
	   this.nodeList = getNodeList(root,nodeList);
	   this.ancestorMap = requiredAncestor;
	   startSimulation();
	   
    }
    private void startSimulation()
    {
	   for(Node node : nodeList)
	   {
		   if(node.isOutput())
		   {
			   simOutputNode(node);
			   continue;
		   }
		   else if(node.isLeaf())
		   {
			   simLeafNode(node);
			   continue;
		   }
		   else
		   {
			   
			   simInternalNode(node);
		   }
	   }
   }
	private  void simInternalNode(Node node) {
	// TODO Auto-generated method stub
		String type = node.getType();
		List<Node> simList = node.getSimList();
		for(Node tempNode : nodeList)
		{
			if(tempNode.getType().equals(type) &&
					nodeInCpar(tempNode, node) && nodeInAuganc(tempNode,node))
				node.addSim(tempNode);
		}
		node.setCPar(computeCparent(simList));
		node.setAuganc(computeAnc(simList));
		//compute auganc
	}
	private boolean nodeInAuganc(Node testNode, Node parent) {
		// TODO Auto-generated method stub
		List<Node> dChildList = parent.getDecendant();
		boolean ret = true;
		for(Node dChildNode : dChildList)
		{
			if(!(dChildNode).getAuganc().contains(testNode))
				ret = false;
		}
		return ret;
	}
	private boolean nodeInCpar(Node testNode, Node parent) {
		// TODO Auto-generated method stub
		List<Node> cChildList = parent.getChild();
		boolean ret = true;
		for(Node cChildNode : cChildList)
		{
		   	if(!cChildNode.getCPar().contains(testNode))
		   		ret = false;
		}
		return ret;
	}
	private void simLeafNode(Node node) {
	// TODO Auto-generated method stub
		String type = node.getType();
		List<Node> simList = node.getSimList();
		for(Node tempNode : nodeList)
		{   
			if(tempNode.getType().equals(type))
			{
				node.addSim(tempNode);
			}
		}//calculate sim for the leaf node
		node.setCPar(computeCparent(simList));
	     //compute auganc
		List<Node> auganc = computeAnc(simList);
		List<Node> lhsList = getLHSFromIC(ancestorMap, node);
		List<Node> lhsAnc = null;
		if(lhsList != null)
		{   
			lhsAnc = computeAnc(lhsList);
			auganc.addAll(lhsList);
		}
		if(lhsAnc !=null)
		auganc.addAll(lhsAnc);
		node.setAuganc(auganc);
}
	private List<Node> getLHSFromIC(HashMap<String, List<String>> ancestorMap,Node node) {
		// TODO Auto-generated method stub
		List<String> lhsList = ancestorMap.get(node.getType());
		if(lhsList == null) return null;
		return convertStringToNode(lhsList);
		
	}
	private List<Node> convertStringToNode(List<String> lhsList) {
		// TODO Auto-generated method stub
		List<Node> retList = new ArrayList<Node>();
		for(Node node : nodeList)
		{
			if(lhsList.contains(node.getType()))
			{
				retList.add(node);
			}
		}
		
		return retList;
	}
	private void simOutputNode(Node node) {
	// TODO Auto-generated method stub
		List<Node> simList = node.getSimList();
		simList.add(node);//set the current node a simulation in its sim[] array
	    node.setCPar(computeCparent(simList));
	    //compute auganc
		node.setAuganc(computeAnc(simList));
}
	private  List<Node> computeCparent(List<Node> simList) {
		// TODO Auto-generated method stub
		List<Node> retList = new ArrayList<Node>();
		for(Node node : simList)
		{
			Node parent = node.getParent();
			if(parent != null)
			retList.add(parent);
		}
		return retList;
	}
	private List<Node> computeAnc(List<Node> simList)
	{
		List<Node> retList = new ArrayList<Node>();
		for(Node node : simList)
		{   List<Node> tempList = new ArrayList<Node>();
			retList.addAll(node.getAncestorList(tempList, node));
		}
		return retList;
	}
	private List<Node> getNodeList(Node parent,List<Node> nodeList) 
	{
		if(parent == null) return nodeList;
		List<Node> childList = parent.getChild();
		List<Node> descendantList = parent.getDecendant();
		if(!childList.isEmpty())
	    {
		   for(Node child : childList)
		   {
	 		getNodeList(child,nodeList);
	     	}
	    }
		if(!descendantList.isEmpty())	
	    {
		   for(Node descendant : descendantList)
		   {
	 		getNodeList(descendant,nodeList);
	     	}
	    }
		nodeList.add(parent);
		return nodeList;
	}
	public static void toString(Node node, int level)
	{
		System.out.println(node.getType()+".     (ID: " +node.toString()+")");
		String blank ="  ";
		for(int i=0; i<level;i++)
			blank += blank;
		List<Node> childList = node.getChild();
		List<Node> descendantList = node.getDecendant();
		int temp = ++level;
		if(!childList.isEmpty())
		{
			for(Node child : childList){
		    System.out.print(blank+"c-child:  ");
		    toString(child,temp);}
		}
		if(!descendantList.isEmpty())
		{
			for(Node descendant : descendantList){
				System.out.print(blank+"d-child:  ");
		    toString(descendant,temp);}
		}
	}
}
